﻿using System.Linq;

namespace BorgExt.Behaviors
{
    public class PlugBehavior : Behavior<CompositionEventArgs>
    {
        public PlugBehavior(IRule<CompositionEventArgs> rule)
            : base(rule)
        { }

        public override void Bind(Slot slot)
        {
            base.Bind(slot);

            Composer.ExtensionCreated += ExtensionCreated;
            Composer.ExtensionDestroyed += ExtensionDestroyed;

            if (!slot.IsOpen())
                return;

            CompositionEventArgs args;

            foreach (var plug in InstanceStore.GetPlugs(slot.Name))
            {
                args = new CompositionEventArgs(slot, plug);
                Rule.AddCandidate(args);
            }

            foreach (var plug in slot.PluggedPlugs)
            {
                args = new CompositionEventArgs(slot, plug);
                Rule.Composed(args);
            }
        }

        public override bool CanPlug(CompositionEventArgs args, out LogEventArgs log)
        {
            return Rule.CanCompose(args, out log);
        }

        public override bool CanUnplug(CompositionEventArgs args, out LogEventArgs log)
        {
            return Rule.CanDecompose(args, out log);
        }

        public override void Closed(SlotEventArgs args)
        {
            foreach (var plug in InstanceStore.GetPlugs(args.Slot.Name))
                Rule.RemoveCandidate(new CompositionEventArgs(args.Slot, plug));
        }

        protected override void Compose(CompositionEventArgs args)
        {
            args.Slot.Plug(args.Plug);
        }

        protected override void Decompose(CompositionEventArgs args)
        {
            args.Slot.Unplug(args.Plug);
        }

        private void ExtensionCreated(ExtensionEventArgs args)
        {
            if (BehaviorSlot != null)
            {
                if (BehaviorSlot.IsOpen() &&
                    args.Extension.Plugs.Contains(BehaviorSlot.Name))
                    Rule.AddCandidate(
                        new CompositionEventArgs(
                            BehaviorSlot, args.Extension.Plugs[BehaviorSlot.Name]));
            }
            else
                foreach (var plug in args.Extension.Plugs)
                    foreach (var slot in
                        from slot in InstanceStore.GetSlots(plug.Name)
                        where slot.IsOpen()
                        select slot)
                        Rule.AddCandidate(new CompositionEventArgs(slot, plug));
        }

        private void ExtensionDestroyed(ExtensionEventArgs args)
        {
            if (BehaviorSlot != null)
            {
                if (BehaviorSlot.IsOpen() &&
                    args.Extension.Plugs.Contains(BehaviorSlot.Name))
                    Rule.RemoveCandidate(
                        new CompositionEventArgs(
                            BehaviorSlot, args.Extension.Plugs[BehaviorSlot.Name]));
            }
            else
                foreach (var plug in args.Extension.Plugs)
                    foreach (var slot in
                        from slot in InstanceStore.GetSlots(plug.Name)
                        where slot.IsOpen()
                        select slot)
                        Rule.RemoveCandidate(new CompositionEventArgs(slot, plug));
        }

        public override void Opened(SlotEventArgs args)
        {
            foreach (var plug in InstanceStore.GetPlugs(args.Slot.Name))
                Rule.AddCandidate(new CompositionEventArgs(args.Slot, plug));
        }

        public override void Plugged(CompositionEventArgs args)
        {
            Rule.Composed(args);
        }

        public override void Plugging(CompositionEventArgs args)
        {
            Rule.Composing(args);
        }

        public override void Unbind(Slot slot)
        {
            Composer.ExtensionCreated -= ExtensionCreated;
            Composer.ExtensionDestroyed -= ExtensionDestroyed;
            
            base.Unbind(slot);
        }

        public override void Unplugged(CompositionEventArgs args)
        {
            Rule.Decomposed(args);
        }

        public override void Unplugging(CompositionEventArgs args)
        {
            Rule.Decomposing(args);
        }
    }
}

